package math;

import math.base.Coord2d;
import math.base.Coord3d;
import math.base.Coord4d;
import math.base.Point2d;
import math.base.Point3d;

/**
 * @author Saga
 *
 */
public class BSplineUtils {

    public static final boolean isRational(final double[] weights) {
        final int length = weights.length;
        for (int id = 0; id < length; ++id) {
            if (weights[id] <= 0.0) {
                throw new ArithmeticException("");
            }

            if (weights[id] != 1.0) {
                return true;
            }
        }

        return false;
    }

    public static final Point2d deCasteljau2d(final Point2d[] _poles, final double _param) {
        final int length = _poles.length;
        final Coord2d[] temp = new Coord2d[length];
        for (int id = 0; id < length; ++id) {
            temp[id] = _poles[id].getCoord().clone();
        }

        final double compParm = 1.0 - _param;
        for (int id = 1; id <= length; ++id) {
            for (int jd = 0; jd <= length - id; ++jd) {
                temp[jd] = temp[jd].multiplied(compParm).added(temp[jd + 1].multiplied(_param));
            }
        }

        return new Point2d(temp[0]);
    }

    public static final Point2d deCasteljauWithWeights2d(final Point2d[] _poles,
            final double[] _weights,
            final double _param) {
        final int length = _poles.length;
        final Coord3d[] temp = new Coord3d[length];
        Coord2d tempCoord = new Coord2d();
        for (int id = 0; id < length; ++id) {
            tempCoord = _poles[id].getCoord();
            temp[id] = new Coord3d(tempCoord.getX(), tempCoord.getY(), _weights[id]);
        }

        final double compParm = 1.0 - _param;
        for (int id = 1; id <= length; ++id) {
            for (int jd = 0; jd <= length - id; ++jd) {
                temp[jd] = temp[jd].multiplied(compParm).added(temp[jd + 1].multiplied(_param));
            }
        }

        tempCoord.init(temp[0].getX(), temp[0].getY());
        return new Point2d(tempCoord.divided(temp[0].getZ()));
    }

    public static final Point3d deCasteljau3d(final Point3d[] _poles, final double _param) {
        final int length = _poles.length;
        final Coord3d[] temp = new Coord3d[length];
        for (int id = 0; id < length; ++id) {
            temp[id] = _poles[id].getCoord().clone();
        }

        final double compParm = 1.0 - _param;
        for (int id = 1; id <= length; ++id) {
            for (int jd = 0; jd <= length - id; ++jd) {
                temp[jd] = temp[jd].multiplied(compParm).added(temp[jd + 1].multiplied(_param));
            }
        }

        return new Point3d(temp[0]);
    }

    public static final Point3d deCasteljauWithWeights3d(final Point3d[] _poles,
            final double[] _weights, final double _param) {
        final int length = _poles.length;
        final Coord4d[] temp = new Coord4d[length];
        Coord3d tempCoord = new Coord3d();
        for (int id = 0; id < length; ++id) {
            tempCoord = _poles[id].getCoord();
            temp[id] = new Coord4d(tempCoord.getX(), tempCoord.getY(), tempCoord.getZ(),
                    _weights[id]);
        }

        final double compParm = 1.0 - _param;
        for (int id = 1; id <= length; ++id) {
            for (int jd = 0; jd <= length - id; ++jd) {
                temp[jd] = temp[jd].multiplied(compParm).added(temp[jd + 1].multiplied(_param));
            }
        }

        tempCoord.init(temp[0].getX(), temp[0].getY(), temp[0].getZ());
        return new Point3d(tempCoord.divided(temp[0].getW()));
    }
}
